package server

import (
	"fmt"
	"log"
	"sync"

	"github.com/pion/rtp"
	"github.com/pion/webrtc/v3"
	"github.com/pion/webrtc/v3/pkg/media"
)

type WeSocketWebrtcConnectionPull struct {
	*WeSocketWebrtcConnection

	remoteWebrtc      *WeSocketWebrtcConnectionPush // 源流webrtc
	remoteRtspSession *RtspSession                  // 源流rtsp

	receiverTracker       []*webrtc.TrackLocalStaticRTP    // 引用的tracker
	receiverTrackerSample []*webrtc.TrackLocalStaticSample // 引用的tracker
	receiverTrackerMux    sync.RWMutex
}

func (tis *WeSocketWebrtcConnectionPull) Close() {
	log.Printf("关闭拉流: %v %v", tis.Name, tis.UID)

	if tis.remoteWebrtc != nil {
		tis.remoteWebrtc.delReceiver(tis)
	}
	if tis.remoteRtspSession != nil {
		tis.remoteRtspSession.delReceiver(tis)
	}

	if tis.pc != nil {
		_ = tis.pc.Close()
	}

	if tis.ws != nil {
		_ = tis.ws.Close()
	}
}

// WriteRTP 发送rtp
func (tis *WeSocketWebrtcConnectionPull) WriteRTP(fromTracker *webrtc.TrackRemote, pkt *rtp.Packet) error {
	if tis.receiverTracker == nil || len(tis.receiverTracker) == 0 {
		return nil
	}

	tis.receiverTrackerMux.RLock()
	defer tis.receiverTrackerMux.RUnlock()

	for _, tracker := range tis.receiverTracker {
		if fromTracker.Kind() == tracker.Kind() {
			return tracker.WriteRTP(pkt)
		}
	}

	return nil
}

// WriteRtpOfRTSP 发送 rtp
func (tis *WeSocketWebrtcConnectionPull) WriteRtpOfRTSP(pkt *rtp.Packet) error {
	if tis.receiverTracker == nil || len(tis.receiverTracker) == 0 {
		return nil
	}

	tis.receiverTrackerMux.RLock()
	defer tis.receiverTrackerMux.RUnlock()

	for _, tracker := range tis.receiverTracker {
		if tracker.Kind() == webrtc.RTPCodecTypeVideo {
			// log.Printf("write %v", pkt.SequenceNumber)
			return tracker.WriteRTP(pkt)
		}
	}

	return nil
}

// WriteSampleOfRTSP 发送 sample
func (tis *WeSocketWebrtcConnectionPull) WriteSampleOfRTSP(kind webrtc.RTPCodecType, sample media.Sample) error {
	if tis.receiverTrackerSample == nil || len(tis.receiverTrackerSample) == 0 {
		return nil
	}

	tis.receiverTrackerMux.RLock()
	defer tis.receiverTrackerMux.RUnlock()

	for _, tracker := range tis.receiverTrackerSample {
		if tracker.Kind() == kind {
			return tracker.WriteSample(sample)
		}
	}

	return nil
}

func (tis *WeSocketWebrtcConnectionPull) Write(fromTracker *webrtc.TrackRemote, data []byte) (n int, err error) {
	if tis.receiverTracker == nil || len(tis.receiverTracker) == 0 {
		return len(data), nil
	}

	tis.receiverTrackerMux.RLock()
	defer tis.receiverTrackerMux.RUnlock()

	for _, tracker := range tis.receiverTracker {
		if fromTracker.Kind() == tracker.Kind() {
			return tracker.Write(data)
		}
	}

	return len(data), nil
}

func (tis *WeSocketWebrtcConnectionPull) OnRequest(req *JsonProtocol) {
	if tis == nil || req == nil {
		return
	}

	log.Printf("收到请求: %v", req.Method)

	switch req.Method {
	case Offer:
		var err error
		if req.Offer.Rtsp {
			err = tis.onPullOfferRtsp(req.Offer.Sample, req.Offer.Name, req.Offer.Offer)
		} else {
			err = tis.onPullOffer(req.Offer.Name, req.Offer.Offer)
		}

		if err != nil {
			log.Println(err)
		}
	case Candidate:
	}
}

// onPullOffer 拉流请求
func (tis *WeSocketWebrtcConnectionPull) onPullOffer(name string, offer webrtc.SessionDescription) error {
	api := tis.engine.api

	remoteConn := tis.engine.GetWebrtcConnection(name)
	if remoteConn == nil {
		_ = tis.ws.WriteJSON(&JsonProtocol{
			Method: Answer,
			Answer: struct {
				Code   int                        `json:"code"`
				MSG    string                     `json:"msg"`
				Answer *webrtc.SessionDescription `json:"answer"`
			}{
				Code: -1,
				MSG:  "not found",
			},
		})
		return fmt.Errorf("not found")
	}

	// 拉取 推流端 的所有流
	receiverTrackerList := remoteConn.NewTrackerRTP([]webrtc.RTPCodecType{webrtc.RTPCodecTypeVideo, webrtc.RTPCodecTypeAudio})
	if len(receiverTrackerList) == 0 {
		_ = tis.ws.WriteJSON(&JsonProtocol{
			Method: Answer,
			Answer: struct {
				Code   int                        `json:"code"`
				MSG    string                     `json:"msg"`
				Answer *webrtc.SessionDescription `json:"answer"`
			}{
				Code: -1,
				MSG:  "no stream",
			},
		})
		return fmt.Errorf("no stream")
	}

	peerConnection, err := api.NewPeerConnection(webrtc.Configuration{
		ICEServers: []webrtc.ICEServer{
			{
				URLs: []string{"stun:stun.l.google.com:19302"},
			},
		},
	})
	if err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("NewPeerConnection")
	}

	// 添加流 AddTrack
	for _, rtpTracker := range receiverTrackerList {
		if _, err = peerConnection.AddTrack(rtpTracker); err != nil {
			log.Printf("AddTrack fail. %v", err)
			continue
		}

		tis.receiverTrackerMux.Lock()
		tis.receiverTracker = append(tis.receiverTracker, rtpTracker)
		tis.receiverTrackerMux.Unlock()
	}

	peerConnection.OnSignalingStateChange(func(state webrtc.SignalingState) {
		log.Printf("状态变化: %v", state.String())
	})

	// Set the remoteWebrtc SessionDescription
	if err = peerConnection.SetRemoteDescription(offer); err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("SetRemoteDescription")
	}

	// Create answer
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("CreateAnswer")
	}

	// Create channel that is blocked until ICE Gathering is complete
	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

	// Sets the LocalDescription, and starts our UDP listeners
	if err = peerConnection.SetLocalDescription(answer); err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("SetLocalDescription")
	}

	// Block until ICE Gathering is complete, disabling trickle ICE
	// we do this because we only can exchange one signaling message
	// in a production application you should exchange ICE Candidates via OnICECandidate
	<-gatherComplete

	log.Printf("创建pull流成功 [%v]", name)

	remoteConn.addReceiver(tis)
	tis.remoteWebrtc = remoteConn
	tis.pc = peerConnection

	err = tis.ws.WriteJSON(&JsonProtocol{
		Method: Answer,
		Answer: struct {
			Code   int                        `json:"code"`
			MSG    string                     `json:"msg"`
			Answer *webrtc.SessionDescription `json:"answer"`
		}{
			Code:   0,
			MSG:    "success",
			Answer: peerConnection.LocalDescription(),
		},
	})
	if err != nil {
		log.Println(err)
		return err
	}

	return nil
}

// onPullOfferRtsp 拉流请求(rtsp流)
func (tis *WeSocketWebrtcConnectionPull) onPullOfferRtsp(sample bool, name string, offer webrtc.SessionDescription) error {
	api := tis.engine.api

	remoteConn := tis.engine.GetRtspConnection(name)
	if remoteConn == nil {
		_ = tis.ws.WriteJSON(&JsonProtocol{
			Method: Answer,
			Answer: struct {
				Code   int                        `json:"code"`
				MSG    string                     `json:"msg"`
				Answer *webrtc.SessionDescription `json:"answer"`
			}{
				Code: -1,
				MSG:  "not found",
			},
		})
		return fmt.Errorf("not found")
	}

	// 拉取 推流端 的所有流
	var receiverTrackerList []webrtc.TrackLocal
	if sample {
		trackerList := remoteConn.NewTrackerSample([]webrtc.RTPCodecType{webrtc.RTPCodecTypeVideo, webrtc.RTPCodecTypeAudio})
		if len(trackerList) == 0 {
			_ = tis.ws.WriteJSON(&JsonProtocol{
				Method: Answer,
				Answer: struct {
					Code   int                        `json:"code"`
					MSG    string                     `json:"msg"`
					Answer *webrtc.SessionDescription `json:"answer"`
				}{
					Code: -1,
					MSG:  "no stream",
				},
			})
			return fmt.Errorf("no stream")
		} else {
			tis.receiverTrackerMux.Lock()
			tis.receiverTrackerSample = append(tis.receiverTrackerSample, trackerList...)
			tis.receiverTrackerMux.Unlock()
		}

		for _, t := range trackerList {
			receiverTrackerList = append(receiverTrackerList, t)
		}
	} else {
		trackerList := remoteConn.NewTrackerRTP([]webrtc.RTPCodecType{webrtc.RTPCodecTypeVideo, webrtc.RTPCodecTypeAudio})
		if len(trackerList) == 0 {
			_ = tis.ws.WriteJSON(&JsonProtocol{
				Method: Answer,
				Answer: struct {
					Code   int                        `json:"code"`
					MSG    string                     `json:"msg"`
					Answer *webrtc.SessionDescription `json:"answer"`
				}{
					Code: -1,
					MSG:  "no stream",
				},
			})
			return fmt.Errorf("no stream")
		} else {
			tis.receiverTrackerMux.Lock()
			tis.receiverTracker = append(tis.receiverTracker, trackerList...)
			tis.receiverTrackerMux.Unlock()
		}

		for _, t := range trackerList {
			receiverTrackerList = append(receiverTrackerList, t)
		}
	}

	peerConnection, err := api.NewPeerConnection(webrtc.Configuration{
		ICEServers: []webrtc.ICEServer{
			{
				URLs: []string{"stun:stun.l.google.com:19302"},
			},
		},
	})
	if err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("NewPeerConnection")
	}

	// 添加流 AddTrack
	for _, rtpTracker := range receiverTrackerList {
		if _, err = peerConnection.AddTrack(rtpTracker); err != nil {
			log.Printf("AddTrack fail. %v", err)
			continue
		}
	}

	peerConnection.OnSignalingStateChange(func(state webrtc.SignalingState) {
		log.Printf("状态变化: %v", state.String())
	})

	// Set the remoteWebrtc SessionDescription
	if err = peerConnection.SetRemoteDescription(offer); err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("SetRemoteDescription")
	}

	// Create answer
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("CreateAnswer")
	}

	// Create channel that is blocked until ICE Gathering is complete
	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

	// Sets the LocalDescription, and starts our UDP listeners
	if err = peerConnection.SetLocalDescription(answer); err != nil {
		log.Println(err)
		return err
	} else {
		log.Println("SetLocalDescription")
	}

	// Block until ICE Gathering is complete, disabling trickle ICE
	// we do this because we only can exchange one signaling message
	// in a production application you should exchange ICE Candidates via OnICECandidate
	<-gatherComplete

	log.Printf("创建pull流成功 [%v]", name)

	remoteConn.addReceiver(tis)
	tis.remoteRtspSession = remoteConn
	tis.pc = peerConnection

	answerSDP := peerConnection.LocalDescription()

	err = tis.ws.WriteJSON(&JsonProtocol{
		Method: Answer,
		Answer: struct {
			Code   int                        `json:"code"`
			MSG    string                     `json:"msg"`
			Answer *webrtc.SessionDescription `json:"answer"`
		}{
			Code:   0,
			MSG:    "success",
			Answer: answerSDP,
		},
	})
	if err != nil {
		log.Println(err)
		return err
	}

	return nil
}
